﻿using Batman.Authentication.Dtos;
using Batman.Authentication.Utils;
using Batman.Core;
using Batman.Core.Utilities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Net;

namespace Batman.Authentication
{
    public class AuthorizationFilter : IAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            var actionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;

            // 检查控制器是否有AllowAnonymousAttribute特性
            var isAllowAnonymousAttribute = actionDescriptor.ControllerTypeInfo.GetCustomAttributes(typeof(AllowAnonymousAttribute), false).Any();
            if (isAllowAnonymousAttribute) return;

            // 检查action是否有AllowAnonymousAttribute特性
            isAllowAnonymousAttribute = actionDescriptor.MethodInfo.GetCustomAttributes(typeof(AllowAnonymousAttribute), false).Any();
            if (isAllowAnonymousAttribute) return;

            // 从请求头中获取token
            var token = GetToken(context.HttpContext);

            if (string.IsNullOrEmpty(token))
            {
                context.Result = new ObjectResult(new ResponseBase(401, "未登录"));
                return; // TODO: 没TOKEN限制访问
            }

            var securityToken = Utils.JwtTokenUtil.ParseToken(token);
            var roleIds = GetRoleIds(securityToken);

            var urls = new List<string>();

            foreach (var roleId in roleIds)
            {
                urls.AddRange(GetRoleResources(roleId));
            }

            var requestPath = context.HttpContext.Request.Path.Value.ToLower();
            if (urls.Any(t => t.ToLower() == requestPath)) return; // 有权限

            // 没有权限

            var isView = actionDescriptor.MethodInfo.DeclaringType == typeof(ViewResult);
            if (isView)
                context.Result = new RedirectResult("/Login"); // 登陆界面
            else
                context.Result = new ObjectResult(new ResponseBase(401, "无访问权限"));
        }

        private bool IsApi(ExceptionContext context)
        {
            var controllerActionDesc = context.ActionDescriptor as ControllerActionDescriptor;
            var attribute = controllerActionDesc
                            .ControllerTypeInfo
                            .CustomAttributes
                            .FirstOrDefault(c => c.AttributeType == typeof(ApiControllerAttribute));

            return attribute == null ? false : true;
        }

        /// <summary>
        /// 从请求头中获取token
        /// </summary>
        /// <param name="httpContext"></param>
        /// <returns></returns>
        string GetToken(HttpContext httpContext)
        {
            var authenticationInfo = httpContext.Request.Headers["Authorization"];
            var token = authenticationInfo.FirstOrDefault();
            token = string.IsNullOrEmpty(token) ? string.Empty : token.Replace("Bearer ", string.Empty);
            return token;
        }

        /// <summary>
        /// 获取角色的ID
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        List<string> GetRoleIds(JwtSecurityToken token)
        {
            var roleIds = token.Claims.FirstOrDefault(t => t.Type == "roleId")?.Value ?? string.Empty;
            var roleIdList = roleIds.Split('|').Where(t => !string.IsNullOrEmpty(t)).ToList();

            return roleIdList;
        }

        /// <summary>
        /// 获取角色在当前系统中的权限
        /// </summary>
        /// <param name="roleId"></param>
        /// <returns></returns>
        List<string> GetRoleResources(string roleId)
        {
            var appCode = ApplicationOptions.AppCode;
            var rmsUrl = ApplicationOptions.RmsUrl;

            using (var client = new WebClient())
            {
                var url = $"{rmsUrl.TrimEnd('/')}/api/RoleResource?appCode={appCode}&roleId={roleId}";
                var responseText = client.DownloadString(url);
                var urls = JsonUtil.Parse<ResponseBase<List<string>>>(responseText).Result;

                return urls;
            }
        }
    }
}
